<?php

namespace app\common\lib;


/**
 * Jwt验证库
 */
class Jwt {

    //头部
    private static $_header = array(
        //生成signature的算法
        'alg' => 'sha256',
        //类型
        'typ' => 'JWT'
    );

    // jwt官方定义字段，防止冲突异常，生成时，直接过滤掉
    private static $_officialFields = array(
        'iss',
        'iat',
        'exp',
        'nbf',
        'sub',
        'aud',
        'jti'
    );

    //使用HMAC生成信息摘要时所使用的密钥
    private static $_secret = "123456";

    /**
     * 获取jwt token
     * array $payload jwt载荷    格式如下非必须
     * [
     ************************ 官方字段 *******************
     *    'iss'        => 'jwt_admin',                // 签发人
     *    'iat'        => time(),                        // 签发时间
     *    'exp'        => time()+7200,                // 过期时间
     *    'nbf'        => time()+60,                    // 生效时间
     *    'sub'        => 'www.admin.com',            // 主题
     *    'aud'        => 'xxx',                        // 受众
     *    'jti'        => md5(uniqid('JWT').time())    // 该Token唯一标识
     ************************ 自定义字段 *******************
     *    'uid'        => 1,
     *    'en_name'    => 'english name',
     *    'zh_name'    => 'chinese name',
     *    'group_id'    => 1,
     *    'group_name'=> '管理员'
     * ]
     * @param array $adminInfo 加密数据
     * @param string $_secret
     * @param int $expire 过期时间 秒
     * @return bool|string
     */
    public static function getToken($adminInfo = array(), $_secret = '', $expire = 86400) {
        $_secret = $_secret ?: self::$_secret;
        if (empty($adminInfo) || !is_array($adminInfo)) {
            return false;
        }
        // 滤掉特殊字段
        foreach ($adminInfo as $key => $val) {
            if (in_array($key, self::$_officialFields)) {
                unset($adminInfo[$key]);
            }
        }
        if (count($adminInfo) == 0) {
            return false;
        }
        $time = time();
        $base = array(
            'iss' => $_SERVER['HTTP_HOST'],
            'iat' => $time,
            'exp' => $time + $expire ?: 86400,
            'nbf' => $time,
            'sub' => 'admin.account.info',
            'aud' => 'company.users',
            'jti' => md5(uniqid('JWT') . json_encode($adminInfo) . $time)
        );
        $payload = array_merge($base, $adminInfo);
        if (is_array($payload) && !empty($payload)) {
            $base64header = self::base64UrlEncode(json_encode(self::$_header, JSON_UNESCAPED_UNICODE));
            $base64payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
            return $base64header . '.' . $base64payload . '.' . self::signature($base64header . '.' . $base64payload, $_secret, self::$_header['alg']);
        }
        return false;
    }

    /**
     * 验证token是否有效,默认验证exp,nbf,iat时间
     * @param string $Token 需要验证的token
     * @param string $_secret 秘钥
     * @return bool|string
     */
    public static function verifyToken($Token, $_secret = '') {
        $_secret = $_secret ?: self::$_secret;
        $tokens = explode('.', $Token);
        if (count($tokens) != 3) {
            return false;
        }

        list($base64header, $base64payload, $sign) = $tokens;

        // 获取jwt算法
        $base64DecodeHeader = json_decode(self::base64UrlDecode($base64header), JSON_OBJECT_AS_ARRAY);
        if (empty($base64DecodeHeader['alg'])) {
            return false;
        }

        // 签名验证
        if (self::signature($base64header . '.' . $base64payload, $_secret, self::$_header['alg']) !== $sign) {
            return false;
        }

        $payload = json_decode(self::base64UrlDecode($base64payload), JSON_OBJECT_AS_ARRAY);

        // 签发时间大于当前服务器时间验证失败
        if (isset($payload['iat']) && $payload['iat'] > time()) {
            return false;
        }

        // 过期时间小于当前服务器时间验证失败
        if (isset($payload['exp']) && $payload['exp'] < time()) {
            return false;
        }

        // 该nbf时间之前不接收处理该Token
        if (isset($payload['nbf']) && $payload['nbf'] > time()) {
            return false;
        }

        // 过滤官方字段，直接返回业务定义需要的字段
        foreach ($payload as $key => $val) {
            if (in_array($key, self::$_officialFields)) {
                unset($payload[$key]);
            }
        }
        return $payload;
    }

    /**
     * base64UrlEncode    https://jwt.io/ 中base64UrlEncode编码实现
     * @param string $input 需要编码的字符串
     * @return string
     */
    private static function base64UrlEncode($input) {
        return str_replace('=', '', strtr(base64_encode($input), '+/', '-_'));
    }

    /**
     * base64UrlEncode https://jwt.io/ 中base64UrlEncode解码实现
     * @param string $input 需要解码的字符串
     * @return bool|string
     */
    private static function base64UrlDecode($input) {
        $remainder = strlen($input) % 4;
        if ($remainder) {
            $addLen = 4 - $remainder;
            $input .= str_repeat('=', $addLen);
        }
        return base64_decode(strtr($input, '-_', '+/'));
    }

    /**
     * HMACSHA256签名    https://jwt.io/ 中HMACSHA256签名实现
     * @param string $input 为base64UrlEncode(header).".".base64UrlEncode(payload)
     * @param string $key
     * @param string $alg 算法方式
     * @return mixed
     */
    private static function signature($input, $key, $alg = 'sha256') {
        return self::base64UrlEncode(hash_hmac($alg, $input, $key, true));
    }
}
